﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TJDevHouse.Starcraft.MapAnalyser
{
	/// <summary>
	/// Node for pathfinding graph
	/// </summary>
	public class PathFindingNode
	{
		//costs for G score
		const int MOVE_COST_STRAIGHT = 10;
		const int MOVE_COST_DIAG = 14;

		//node coords
		public int X { get; private set; }
		public int Y { get; private set; }

		private int _g = -1;
		private int _h = -1;

		private PathFindingNode _parent;

		/// <summary>
		/// The parent of this node
		/// </summary>
		public PathFindingNode Parent
		{
			get
			{
				return _parent;
			}
			set
			{
				_parent = value;
				//the g score will prolly change on parent change
				_g = -1;
			}
		}

		/// <summary>
		/// Create this node without specifying a parent
		/// </summary>
		/// <param name="x"></param>
		/// <param name="y"></param>
		public PathFindingNode(int x, int y)
		{
			X = x;
			Y = y;
		}

		/// <summary>
		/// Create this node with a specific parent.
		/// </summary>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <param name="parentNode"></param>
		public PathFindingNode(int x, int y, PathFindingNode parentNode)
			: this(x, y)
		{
			Parent = parentNode;
		}

		/// <summary>
		/// Generates the hash to identify this node's coordinates.
		/// </summary>
		/// <returns></returns>
		public string GetCoordHash()
		{
			return X + "," + Y;
		}

		/// <summary>
		/// Get all nodes adjacent to this node.
		/// 
		/// Will set the parent of each node to this node.
		/// </summary>
		/// <returns></returns>
		public List<PathFindingNode> GetAdjacentNodes()
		{
			List<PathFindingNode> nodes = new List<PathFindingNode>();

			//go through all adjacent coords create a new node for each
			//ignoring the current node
			for (int xMod = -1; xMod < 2; xMod++)
			{
				for (int yMod = -1; yMod < 2; yMod++)
				{
					if (xMod != 0 || yMod != 0)
					{
						PathFindingNode n = new PathFindingNode(X + xMod, Y + yMod, this);
						nodes.Add(n);
					}
				}
			}
			return nodes;
		}

		/// <summary>
		/// Find the F score for this node using its parent and the supplied
		/// target node.
		/// </summary>
		/// <param name="targetNode"></param>
		/// <returns></returns>
		public int F(PathFindingNode targetNode)
		{
			int g = G();
			int h = H(targetNode);
			int f = g + h;

			//Console.WriteLine("{0},{1} -> {2},{3} = g:{4} h:{5} f:{6}", X, Y, targetNode.X, targetNode.Y, g, h, _f);

			return f;
		}

		/// <summary>
		/// Calculate the F score of this node using the supplied target and a specific
		/// node to use as the parent.
		/// </summary>
		/// <param name="targetNode"></param>
		/// <param name="testParentNode"></param>
		/// <returns></returns>
		public int F(PathFindingNode targetNode, PathFindingNode testParentNode)
		{
			return G(testParentNode) + H(targetNode);
		}

		/// <summary>
		/// Calculate the G score (movement cost to tile from start point) of this tile.
		/// 
		/// Uses the parent node to trace path.
		/// 
		/// Use G(PathFindingNode) to test G using a different parent.
		/// </summary>
		/// <returns></returns>
		public int G()
		{
			if (Parent == null)
			{
				//no parent, so must be first node
				return 0;
			}

			//only calc when necessary
			if (_g == -1)
			{
				_g = G(Parent);
			}
			return _g;


		}

		/// <summary>
		/// Calculate the G score of this node using the supplied
		/// node as the parent node.
		/// </summary>
		/// <param name="node"></param>
		/// <returns></returns>
		public int G(PathFindingNode node)
		{

			int myG;
			if (IsDiagonalFromNode(node))
			{
				myG = MOVE_COST_DIAG;
			}
			else
			{
				myG = MOVE_COST_STRAIGHT;
			}

			return node.G() + myG;

		}

		/// <summary>
		/// The estimated cost of moving from this node to the
		/// target node
		/// </summary>
		/// <param name="target"></param>
		/// <returns></returns>
		public int H(PathFindingNode target)
		{
			//only calc when necessary
			if (_h == -1)
			{
				//use manhattan method:
				//(number of non diagonal moves from this node to target) * (straight move cost)
				int xMoves = Math.Abs(X - target.X);
				int yMoves = Math.Abs(Y - target.Y);

				_h = (int)Math.Sqrt((xMoves * xMoves) + (yMoves * yMoves)) * MOVE_COST_DIAG;
			}
			return _h;
		}

		/// <summary>
		/// Whether this node is diagonal from the supplied node.
		/// 
		/// Used for G scoring.
		/// </summary>
		/// <param name="node"></param>
		/// <returns></returns>
		public bool IsDiagonalFromNode(PathFindingNode node)
		{
			//if diagonal then will be in a different row and col
			return (X != node.X && Y != node.Y);
		}
	}
}
